package com.powernode.ssm.springAnalysis.a01;

import com.powernode.ssm.springAnalysis.a01.event.TestPublishEvent1;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.scheduling.annotation.EnableScheduling;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;

/*
	4. 学到了什么
		a. BeanFactory 与 ApplicationContext 并不仅仅是简单接口继承的关系, ApplicationContext 组合并扩展了 BeanFactory 的功能
		b. 又新学一种代码之间解耦途径
	练习：完成用户注册与发送短信之间的解耦, 用事件方式、和 AOP 方式分别实现
 */

@SpringBootApplication(scanBasePackages = "com.powernode.ssm.springAnalysis.a01")//提升扫描的范围，让其可以扫描到OtherBean
public class TestA01Application {

	public static void main(String[] args) {
		ConfigurableApplicationContext context = SpringApplication.run(TestA01Application.class, args);
		/**
		 * 选中 ConfigurableApplicationContext 后按 ctrl + alt + U
		 * 可知 ConfigurableApplicationContext的父接口是ApplicationContext
		 * ApplicationContext的顶级父接口为 BeanFactory，这是核心接口，提供bean相关的方法如 getBean
		 * 另外 ApplicationContext还附加了其他四个功能，分别对应其父接口
		 * EnvironmentCapable、ResourcePatternResolver、MessageSource、ApplicationEventPublisher
		 *
		 * 通过debug可知BeanFactory的一个实现类DefaultListableBeanFactory中的一个父类DefaultSingletonBeanRegistry
		 * 中，其成员变量singletonObjects存储了所有的单例bean
		 *
		 */
		/**
		 * 通过反射来获取singletonObjects容器中的单例对象，这里过滤了登录相关的bean对象打印
		 */
		System.out.println("\n===singletonObjects体现===");
        try {
            Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
			singletonObjects.setAccessible(true);
			ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
			ConcurrentHashMap<String, Object> hashMap = (ConcurrentHashMap) singletonObjects.get(beanFactory);
			hashMap.entrySet().stream().filter(e->e.getKey().startsWith("logIn")).forEach(e->{
				System.out.println(e.getKey()+"="+e.getValue());
			});
		} catch (Exception e) {
            e.printStackTrace();
        }


		/**
		 * MessageSource国际化接口，支持多种语言
		 * 体现为resources目录下的 Resource Bundle message中的properties配置文件
		 */
		String hi1 = context.getMessage("hi", null, Locale.CHINA);
		String hi2 = context.getMessage("hi", null, Locale.ENGLISH);
		System.out.println("\n===MessageSource接口体现===");
		System.out.println(hi1);
		System.out.println(hi2);

		/**
		 * ResourcePatternResolver 根据通配符获取一个或多个资源
		 * 类路径用classpath来表示
		 * springboot项目中，放在resource目录下的文件都会在编译后放到类路径中
		 */
		System.out.println("\n===ResourcePatternResolver接口体现===");
		try {
			//classpath: 从类路径中找application.yml文件
            Resource[] resources = context.getResources("classpath:application.yml");
			for (Resource resource : resources) {
				System.out.println(resource);
			}
			//classpath*: 从类路径中以及jar包中找META-INF/spring.factories文件
			Resource[] resourcesAll = context.getResources("classpath*:META-INF/spring.factories");
			for (Resource resource : resourcesAll) {
				System.out.println(resource);
			}
		} catch (IOException e) {
            throw new RuntimeException(e);
        }

		/**
		 * EnvironmentCapable 获取环境变量
		 */
		System.out.println("\n===EnvironmentCapable接口体现===");
		String javaHome = context.getEnvironment().getProperty("java_home");
		String property = context.getEnvironment().getProperty("spring.profiles.active");
		System.out.println("javaHome = " + javaHome);
		System.out.println("property = " + property);

		/**
		 * ApplicationEventPublisher 发布事件和监听事件
		 */
		//context发布TestEvent1类的事件
		//context.publishEvent(new TestEvent1(context));
		System.out.println("\n===ApplicationEventPublisher接口体现===");
		context.getBean(TestPublishEvent1.class).publish();
	}

}
